/*
 * Copyright (c) 2006-2022, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-12-17     Wayne        The first version
 */
//#include "mmu.h"
//#include "nu_2d.h"
#include <lvgl.h>

#if (LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0))
#include "lv_gpu_n9h30_ge2d.h"
#endif

#define LOG_TAG "lvgl.disp"
#define DBG_ENABLE
#define DBG_SECTION_NAME LOG_TAG
#define DBG_LEVEL DBG_ERROR
#define DBG_COLOR
#include <rtdbg.h>

/*A static or global variable to store the buffers*/
static lv_disp_draw_buf_t disp_buf;
static rt_device_t lcd_device = RT_NULL;
static struct rt_device_graphic_info gdi;
static rt_uint32_t gfb_size = 0;
static rt_uint32_t pix_bytes = 0;

static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/

static void rt_disp_full_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p)
{
    if (disp_drv == RT_NULL || disp_drv->draw_buf == RT_NULL || disp_drv->draw_buf->buf1 == RT_NULL) {
        LOG_E("%s: invalid disp_drv", __func__);
        return;
    }
    if (lcd_device == RT_NULL) {
        LOG_E("%s: invalid lcd_device", __func__);
        return;
    }

    rt_err_t err = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &gdi);
    if (err != RT_EOK) {
        LOG_E("%s: get LCD device gdi failed!", __func__);
        return;
    }

    rt_memcpy(gdi.framebuffer, disp_drv->draw_buf->buf1, gfb_size);
    // rt_device_control(lcd_device, RTGRAPHIC_CTRL_RECT_UPDATE, RT_NULL);
    lv_disp_flush_ready(disp_drv);
}

static void rt_disp_flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p)
{
    if (disp_drv == RT_NULL || disp_drv->draw_buf == RT_NULL || disp_drv->draw_buf->buf1 == RT_NULL) {
        LOG_E("%s: invalid disp_drv", __func__);
        return;
    }
    if (lcd_device == RT_NULL) {
        LOG_E("%s: invalid lcd_device", __func__);
        return;
    }

    rt_err_t err = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &gdi);
    if (err != RT_EOK) {
        LOG_E("%s: get LCD device gdi failed!", __func__);
        return;
    }

    if (gdi.framebuffer <= (rt_uint8_t *) color_p && (rt_uint8_t *) color_p < gdi.framebuffer + gfb_size) {
        LOG_W("%s: color_p = %p overleap in gdi.framebuffer: %p + %d", __func__, color_p, gdi.framebuffer, gfb_size);
    }

    int32_t flush_area_w = lv_area_get_width(area);
    int32_t flush_area_h = lv_area_get_height(area);

    /* Update dirty region. */
    uint32_t row_bytes = flush_area_w * pix_bytes;
    for (int i = 0; i < flush_area_h; i++) {
        rt_memcpy((void*)(gdi.framebuffer + ((area->y1 + i) * gdi.width + area->x1) * pix_bytes),
                  (void*)&color_p[i * flush_area_w], row_bytes);
    }

    // rt_device_control(lcd_device, RTGRAPHIC_CTRL_RECT_UPDATE, RT_NULL);
    lv_disp_flush_ready(disp_drv);
}

void lv_port_disp_init(void)
{
    rt_err_t result;
    void* buf1 = RT_NULL;
    void* buf2 = RT_NULL;

    lcd_device = rt_device_find("lcd");
    if (lcd_device == 0) {
        LOG_E("Get LCD device failed!");
        return;
    }

    // get graphic device info
    result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &gdi);
    if (result != RT_EOK) {
        LOG_E("Get LCD device gdi failed!");
        return;
    }

    /* Disable backlight at startup. */
    // rt_device_control(lcd_device, RTGRAPHIC_CTRL_POWEROFF, RT_NULL);

    RT_ASSERT(gdi.bits_per_pixel == 8 || gdi.bits_per_pixel == 16 || gdi.bits_per_pixel == 24 ||
              gdi.bits_per_pixel == 32);

    lv_disp_drv_init(&disp_drv); /*Basic initialization*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = gdi.width;
    disp_drv.ver_res = gdi.height;
    pix_bytes = gdi.bits_per_pixel / 8;
    gfb_size = gdi.width * gdi.height * pix_bytes;
    buf1 = rt_malloc(gfb_size);
    if (buf1 == RT_NULL) {
        LOG_E("alloc %d failed!", gfb_size);
        return;
    }

// #if defined(LV_USE_ANTI_TEARING) && LV_USE_ANTI_TEARING
//     disp_drv.full_refresh = 1;
// #endif

    if (disp_drv.full_refresh) {
        disp_drv.flush_cb = rt_disp_full_flush;
    } else {
        disp_drv.flush_cb = rt_disp_flush;
    }

    /*Initialize `disp_buf` with the buffer(s).*/
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, gdi.width * gdi.height);

    result = rt_device_open(lcd_device, 0);
    if (result != RT_EOK) {
        LOG_E("open LCD device failed!");
        return;
    }

    /*Set a display buffer*/
    disp_drv.draw_buf = &disp_buf;

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

void lv_user_gui_init(void) {}
